#ifndef _WX_CLNTDATAH__
#define _WX_CLNTDATAH__

#include "wx/defs.h"
#include "wx/string.h"
#include "wx/hashmap.h"
#include <tr1/unordered_map>

typedef int ( *wxShadowObjectMethod )( void*, void* );
typedef std::tr1::unordered_map< wxString, void *, wxStringHash, wxStringEqual > wxShadowObjectFields;
typedef std::tr1::unordered_map< wxString, wxShadowObjectMethod, wxStringHash, wxStringEqual > wxShadowObjectMethods;

class wxShadowObject {
  public:
    wxShadowObject() { }

    void AddMethod( const wxString &name, wxShadowObjectMethod method ) {
      wxShadowObjectMethods::iterator it = m_methods.find( name );
      if( it == m_methods.end() ) {
        m_methods[ name ] = method;
      } else{ it->second = method; }
    }

    bool InvokeMethod( const wxString &name, void* window, void* param, int* returnValue ) {
      wxShadowObjectMethods::iterator it = m_methods.find( name );
      if( it == m_methods.end() ) {
        return false;
      }
      wxShadowObjectMethod method = it->second;
      int ret = ( *method )( window, param );
      if( returnValue ) {
        *returnValue = ret;
      }
      return true;
    }

    void AddField( const wxString &name, void* initialValue = NULL ) {
      wxShadowObjectFields::iterator it = m_fields.find( name );
      if( it == m_fields.end() ) {
        m_fields[ name ] = initialValue;
      } else{ it->second = initialValue; }
    }

    void SetField( const wxString &name, void* value ) {
      wxShadowObjectFields::iterator it = m_fields.find( name );
      if( it == m_fields.end() ) {
        return;
      }
      it->second = value;
    }

    void* GetField( const wxString &name, void *defaultValue = NULL ) {
      wxShadowObjectFields::iterator it = m_fields.find( name );
      if( it == m_fields.end() ) {
        return defaultValue;
      }
      return it->second;
    }

  private:
    wxShadowObjectMethods   m_methods;
    wxShadowObjectFields    m_fields;
};

enum wxClientDataType {
  wxClientData_None,    // we don't know yet because we don't have it at all
  wxClientData_Object,  // our client data is typed and we own it
  wxClientData_Void     // client data is untyped and we don't own it
};

class wxClientData {
  public:
    wxClientData() { }
    virtual ~wxClientData() { }
};

class wxStringClientData : public wxClientData {
  public:
    wxStringClientData() : m_data() { }
    wxStringClientData( const wxString &data ) : m_data( data ) { }
    void SetData( const wxString &data ) { m_data = data; }
    const wxString& GetData() const { return m_data; }

  private:
    wxString  m_data;
};

class wxClientDataContainer {
  public:
    wxClientDataContainer();
    virtual ~wxClientDataContainer();
    void SetClientObject( wxClientData *data ) { DoSetClientObject( data ); }
    wxClientData *GetClientObject() const { return DoGetClientObject(); }
    void SetClientData( void *data ) { DoSetClientData( data ); }
    void *GetClientData() const { return DoGetClientData(); }

  protected:
    union {
      wxClientData *m_clientObject;
      void         *m_clientData;
    };
    virtual void DoSetClientObject( wxClientData *data );
    virtual wxClientData *DoGetClientObject() const;
    virtual void DoSetClientData( void *data );
    virtual void *DoGetClientData() const;
    wxClientDataType m_clientDataType;

};

#include "wx/vector.h"

struct wxClientDataDictionaryPair {
  wxClientDataDictionaryPair( size_t idx ) : index( idx ), data( 0 ) { }

  size_t index;
  wxClientData* data;
};

_WX_DECLARE_VECTOR(
  wxClientDataDictionaryPair,
  wxClientDataDictionaryPairVector,
  WXDLLIMPEXP_BASE
);

class wxClientDataDictionary {
  public:
    wxClientDataDictionary() {}
    void DestroyData() {
      for( size_t i = 0, end = m_vec.size(); i != end; ++i ) {
        delete m_vec[i].data;
      }
      m_vec.clear();
    }
    void Set( size_t index, wxClientData* data, bool doDelete ) {
      size_t ptr = Find( index );
      if( !data ) {
        if( ptr == m_vec.size() ) {
          return;
        }
        if( doDelete ) {
          delete m_vec[ptr].data;
        }
        m_vec.erase( ptr );
      } else {
        if( ptr == m_vec.size() ) {
          m_vec.push_back( wxClientDataDictionaryPair( index ) );
          ptr = m_vec.size() - 1;
        }
        if( doDelete ) {
          delete m_vec[ptr].data;
        }
        m_vec[ptr].data = data;
      }
    }

    wxClientData* Get( size_t index ) const {
      size_t it = Find( index );
      if( it == m_vec.size() ) {
        return 0;
      }
      return ( wxClientData* )m_vec[it].data; // const cast
    }

    void Delete( size_t index, bool doDelete ) {
      size_t todel = m_vec.size();
      for( size_t i = 0, end = m_vec.size(); i != end; ++i ) {
        if( m_vec[i].index == index ) {
          todel = i;
        } else if( m_vec[i].index > index ) {
          --( m_vec[i].index );
        }
      }
      if( todel != m_vec.size() ) {
        if( doDelete ) {
          delete m_vec[todel].data;
        }
        m_vec.erase( todel );
      }
    }
  private:
    // returns MyVec.size() if not found
    size_t Find( size_t index ) const {
      for( size_t i = 0, end = m_vec.size(); i != end; ++i ) {
        if( m_vec[i].index == index ) {
          return i;
        }
      }
      return m_vec.size();
    }

    wxClientDataDictionaryPairVector m_vec;
};

#endif // _WX_CLNTDATAH__

